package com.zdp.leetcodeMiddle;
/*
* 题目描述：
* 给定一个二维网格和一个单词，找出该单词是否存在于网格中。
单词必须按照字母顺序，通过相邻的单元格内的字母构成，其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
示例:
board =
[
  ['A','B','C','E'],
  ['S','F','C','S'],
  ['A','D','E','E']
]
给定 word = "ABCCED", 返回 true
给定 word = "SEE", 返回 true
给定 word = "ABCB", 返回 false
提示：
board 和 word 中只包含大写和小写英文字母。
1 <= board.length <= 200
1 <= board[i].length <= 200
1 <= word.length <= 10^3
来源：力扣（LeetCode）
链接：https://leetcode-cn.com/problems/word-search
著作权归领扣网络所有。商业转载请联系官方授权，非商业转载请注明出处。
* */
public class 单词搜索_79 {
    public static void main(String[] args) {
        单词搜索_79 demo = new 单词搜索_79();
        char[][] x = new char[][]{
                {'A','B','C','E'},
                {'S','F','C','S'},
                {'A','D','E','E'}
        };
        String ss = new String("ABCB");
        boolean exist = demo.exist(x, ss);
        System.out.println(exist);
    }

    /*
    * 解题思路：利用深搜和回溯
    * 因为字母不能被重复利用，所以使用一个标记数组来记录使用过的字母
    * 一个单词可以有四种路径选择 上下左右 找到符合条件的那条路，错了就回溯
    * */
    public boolean exist(char[][] board, String word) {
        int[][] visited = new int[board.length][board[0].length];
        char[] words = word.toCharArray();
        // 在这边先找到第一个出现
        boolean flag  = false;
        for(int i=0;i<board.length;i++){
            for(int j=0;j<board[0].length;j++){
                // 找到单词首字母的匹配位置
                if(board[i][j] == words[0]){
                    visited[i][j] = 1;
                    flag = f(board,visited,words,1,i,j);
                    if(flag){
                        return flag;
                    }
                    visited[i][j] = 0;
                }
            }
        }
        return flag;
    }

    public boolean f(char[][] board,int[][] visited,char[] word,int wordIndex,int boardI,int boardJ){
        //判断board的相邻位置是否可以匹配
        if(wordIndex == word.length){
            return true;
        }
        boolean flag  =  false;
        // 上
        if(boardI-1>=0&&board[boardI-1][boardJ]==word[wordIndex]&&visited[boardI-1][boardJ]==0){
            visited[boardI-1][boardJ] = 1;// 记录走过的路
            flag = f(board,visited,word,wordIndex+1,boardI-1,boardJ);
            // 如果退出了
            if(flag){
                // 成功
                return flag;
            }
            // 失败 就继续
            visited[boardI-1][boardJ] = 0;
        }
        // 下
        if(boardI+1<board.length&&board[boardI+1][boardJ]==word[wordIndex]&&visited[boardI+1][boardJ]==0){
            visited[boardI+1][boardJ] = 1;// 记录走过的路
            flag = f(board,visited,word,wordIndex+1,boardI+1,boardJ);
            if(flag){
                // 成功
                return flag;
            }
            visited[boardI+1][boardJ] = 0;
        }
        // 左
        if(boardJ-1>=0&&board[boardI][boardJ-1]==word[wordIndex]&&visited[boardI][boardJ-1]==0){
            visited[boardI][boardJ-1] = 1;// 记录走过的路
            flag = f(board,visited,word,wordIndex+1,boardI,boardJ-1);
            if(flag){
                // 成功
                return flag;
            }
            visited[boardI][boardJ-1] = 0;
        }
        // 右
        if(boardJ+1<board[0].length&&board[boardI][boardJ+1]==word[wordIndex]&&visited[boardI][boardJ+1]==0) {
            visited[boardI][boardJ+1] = 1;// 记录走过的路
            flag =  f(board,visited,word,wordIndex+1,boardI,boardJ+1);
            if(flag){
                // 成功
                return flag;
            }
            visited[boardI][boardJ+1] = 0;
        }
        return flag;
    }

    /*
    * 官方题解 也是深度优先搜索
    * */
    public boolean exist1(char[][] board, String word) {
        int h = board.length, w = board[0].length;
        boolean[][] visited = new boolean[h][w];
        for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
                boolean flag = check(board, visited, i, j, word, 0);
                if (flag) {
                    return true;
                }
            }
        }
        return false;
    }

    public boolean check(char[][] board, boolean[][] visited, int i, int j, String s, int k) {
        if (board[i][j] != s.charAt(k)) {
            return false;
        } else if (k == s.length() - 1) {
            return true;
        }
        visited[i][j] = true;
        int[][] directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
        boolean result = false;
        for (int[] dir : directions) {
            int newi = i + dir[0], newj = j + dir[1];
            if (newi >= 0 && newi < board.length && newj >= 0 && newj < board[0].length) {
                if (!visited[newi][newj]) {
                    boolean flag = check(board, visited, newi, newj, s, k + 1);
                    if (flag) {
                        result = true;
                        break;
                    }
                }
            }
        }
        visited[i][j] = false;
        return result;
    }

}
